using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Diagnostics;
using System.Xml;
using System.Xml.Schema; 
using XPathParser; 
using System.Xml.Linq; 
using System.Xml.XPath;

namespace CodeGenerationPipelineInterface
{
	[DebuggerDisplay("{ DebugInfo }")]
	public class Operation : IXmlElement
	{
        private IXmlDocument document;
        private XNode node;
        private WSDLOperationMappedTo wSDLOperationMappedTo;
        private DataContextOperation dataContextOperation;
        private Initialization initialization;
        private PostProcessing postProcessing;
        private string name; 
        private const int unbounded = int.MaxValue;

		public Operation(IXmlDocument document)
		{
            
            this.document = document;
		}

        public IXmlDocument Document 
        { 
            get
            {
                return document;
            }

            set
            {
                document = value;
            }
        }

		[XPathAttribute("/ElementBuild/Elements/Root/ChildElements/Element/Operations/Operation[@Name]")]
		[UseAttribute(XPathParser.Use.Required)]
		public string Name 
		{
            get
            {
			    return name;
            }

            set
            {
                name = value;
            }
		}

		[XPathAttribute("/ElementBuild/Elements/Root/ChildElements/Element/Operations/Operation/WSDLOperationMappedTo")]
		[MaxOccursAttribute(1)]
		public WSDLOperationMappedTo WSDLOperationMappedTo
		{
			get
			{
				return wSDLOperationMappedTo;
			}
		}

		[XPathAttribute("/ElementBuild/Elements/Root/ChildElements/Element/Operations/Operation/DataContextOperation")]
		[MinOccursAttribute(1)]
		[MaxOccursAttribute(1)]
		public DataContextOperation DataContextOperation
		{
			get
			{
				return dataContextOperation;
			}
		}

		[XPathAttribute("/ElementBuild/Elements/Root/ChildElements/Element/Operations/Operation/Initialization")]
		[MinOccursAttribute(0)]
		[MaxOccursAttribute(1)]
		public Initialization Initialization
		{
			get
			{
				return initialization;
			}
		}

		[XPathAttribute("/ElementBuild/Elements/Root/ChildElements/Element/Operations/Operation/PostProcessing")]
		[MinOccursAttribute(0)]
		[MaxOccursAttribute(1)]
		public PostProcessing PostProcessing
		{
			get
			{
				return postProcessing;
			}
		}

		public string ElementName
		{
			get
			{
				return "Operation";
			}
		}

		public string DebugInfo
		{
			get
			{
				return "Operation";
			}
		}

        public XNode Node
        {
            get
            {
                return node;
            }
        }

        public IEnumerable<string> CDataElements
        {
            get
            {
                var element = (XElement)node;

                return element.Nodes().Where(n => n is XCData).Cast<XCData>().Select(c => c.Value);
            }
        }

        public void Read(XNode node)
        {
            var elementNode = (XElement) node;
            var prefix = elementNode.GetPrefixOfNamespace(elementNode.Name.Namespace);

            // WSDLOperationMappedTo

            var elementWSDLOperationMappedTo = node.XPathSelectElement(prefix + ":WSDLOperationMappedTo", this.Document.NamespaceManager);
            if (elementWSDLOperationMappedTo != null)
            {
                var wSDLOperationMappedTo = new WSDLOperationMappedTo(this.Document);

                wSDLOperationMappedTo.Read(elementWSDLOperationMappedTo);

                this.wSDLOperationMappedTo  = wSDLOperationMappedTo;
            }

            // DataContextOperation

            var elementDataContextOperation = node.XPathSelectElement(prefix + ":DataContextOperation", this.Document.NamespaceManager);
            var dataContextOperation = new DataContextOperation(this.Document);

            dataContextOperation.Read(elementDataContextOperation);

            this.dataContextOperation  = dataContextOperation;

            // Initialization

            var elementInitialization = node.XPathSelectElement(prefix + ":Initialization", this.Document.NamespaceManager);
            if (elementInitialization != null)
            {
                var initialization = new Initialization(this.Document);

                initialization.Read(elementInitialization);

                this.initialization  = initialization;
            }

            // PostProcessing

            var elementPostProcessing = node.XPathSelectElement(prefix + ":PostProcessing", this.Document.NamespaceManager);
            if (elementPostProcessing != null)
            {
                var postProcessing = new PostProcessing(this.Document);

                postProcessing.Read(elementPostProcessing);

                this.postProcessing  = postProcessing;
            }


            // Name

            if (elementNode.Attributes().Any(a => a.Name.LocalName == "Name"))
            {
                name = elementNode.Attribute("Name").Value;
            }
            this.node = node;
        }

        public void Write(XNode parentNode, XNamespace elementNamespace)
        {
            var parentElement = (XElement)parentNode;
            var element = new XElement(elementNamespace + this.ElementName);

            parentElement.Add(element);

            // Name

            element.Add(new XAttribute("Name", this.Name.ToString()));

            var wSDLOperationMappedTo = this.WSDLOperationMappedTo;

            if (wSDLOperationMappedTo != null)
            {
                wSDLOperationMappedTo.Write(element, elementNamespace);
            }
            var dataContextOperation = this.DataContextOperation;

            dataContextOperation.Write(element, elementNamespace);
            var initialization = this.Initialization;

            if (initialization != null)
            {
                initialization.Write(element, elementNamespace);
            }
            var postProcessing = this.PostProcessing;

            if (postProcessing != null)
            {
                postProcessing.Write(element, elementNamespace);
            }
        }

        public void Write(XDocument document, XmlNamespaceManager namespaces)
        {
            var namespaceAttributes = namespaces.Cast<string>().Where(s => !string.IsNullOrEmpty(s) && s != "xmlns" && s != "xml").Select(s => new XAttribute(XNamespace.Xmlns + s, XNamespace.Get(namespaces.LookupNamespace(s))));
            var element = new XElement(this.document.ElementNamespace + this.ElementName, namespaceAttributes.ToArray<object>());

            document.Add(element);

            var wSDLOperationMappedTo = this.WSDLOperationMappedTo;
            wSDLOperationMappedTo.Write(element, this.document.ElementNamespace);

            var dataContextOperation = this.DataContextOperation;
            dataContextOperation.Write(element, this.document.ElementNamespace);

            var initialization = this.Initialization;
            initialization.Write(element, this.document.ElementNamespace);

            var postProcessing = this.PostProcessing;
            postProcessing.Write(element, this.document.ElementNamespace);

        }

        public bool Validate(object sender, ValidationEventHandler handler)
        {
            var valid = true;
            WSDLOperationMappedTo wSDLOperationMappedTo = this.WSDLOperationMappedTo;
            wSDLOperationMappedTo.Validate(sender, (sender2, e) =>
            {
                handler(sender2, e);
                valid = false;
            });

            DataContextOperation dataContextOperation = this.DataContextOperation;
            dataContextOperation.Validate(sender, (sender2, e) =>
            {
                handler(sender2, e);
                valid = false;
            });

            Initialization initialization = this.Initialization;
            initialization.Validate(sender, (sender2, e) =>
            {
                handler(sender2, e);
                valid = false;
            });

            PostProcessing postProcessing = this.PostProcessing;
            postProcessing.Validate(sender, (sender2, e) =>
            {
                handler(sender2, e);
                valid = false;
            });

            return valid;
        }
    }
}
